package dbexp.service;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Vector;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import dbexp.model.Function;
import dbexp.model.NONE;
import dbexp.model.DbObject;
import dbexp.model.Package;
import dbexp.model.PackageBody;
import dbexp.model.Procedure;
import dbexp.model.Schema;
import dbexp.model.Sequence;
import dbexp.model.SourceType;
import dbexp.model.Table;
import dbexp.model.Trigger;
import dbexp.model.View;
import dbexp.model.db.DbOT;
import dbexp.model.db.DbUser;
import dbexp.connection.DbConnection;
import dbexp.connection.DbMetaData;
import dbexp.utils.Unicode;

public class DbTreeServiceImpl extends HttpServlet {
	/**
	 *
	 */
	private static final long serialVersionUID = 1L;
	private HashMap<String, DbObject[]> categories = new HashMap<String, DbObject[]>();
	private HashMap<String, DbObject[]> dbObjects = new HashMap<String, DbObject[]>();
	private DbUser u = null;
	private final String ROOT = "root";

	public void init(ServletConfig config) throws ServletException {
		System.out.println("init");
		initTree();
	}

	public void initTree() {
		categories.clear();
		dbObjects.clear();
	}

	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		doPost(req, resp);
	}

	@SuppressWarnings("unchecked")
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		String response = null;
		String resetTree = req.getParameter("reset");
		HttpSession session = req.getSession(false);
		if (session == null) {
			System.out.println("session null");
		} else if (session.isNew()) {
			System.out.println("Nowa sessja");
		} else {
			u = (DbUser) session.getAttribute("USER");
			if (u != null) {
				if (resetTree != null) {
					initTree();
					resp.setStatus(HttpServletResponse.SC_OK);
					System.out.println("reset");
					return;
				}
				String cmd = req.getParameter("cmd");
				String path = Unicode.decode(req.getParameter("path"));
				String schema = Unicode.decode(req.getParameter("schema"));
				String type = Unicode.decode(req.getParameter("type"));
				String name = Unicode.decode(req.getParameter("name"));

				System.out.println("wartosc cmd:" + cmd);
				System.out.println("wartosc path:" + path);
				System.out.println("wartosc schema:" + schema);
				System.out.println("wartosc name:" + name);
				System.out.println("wartosc type:" + type);
				if (schema == null) {
					schema = "";
				}
				if (cmd == null && path != null) {
					cmd = "get";
				} else if (cmd == null) {
					try {
						JSONObject jo = new JSONObject();
						jo.put("success", false);
						jo.put("error", "Nieznana akcja");
						response = jo.toString();
					} catch (Exception e) {
						response = error("Nieznana akcja");
					}
				}

				if (cmd == null) {
					try {
						JSONObject jo = new JSONObject();
						jo.put("success", false);
						jo.put("error", "Nieznana komenda");
						response = jo.toString();
					} catch (Exception e) {
						response = error("Nieznana komenda");
					}
				} else {
					if (cmd.equals("get")) {
						System.out.println("get");
						response = doGetDbObjects(path, schema);
					} else if (cmd.equals("gets")) {
						System.out.println("gets");
						response = doGetDbObjects(path, schema);
					} else if (cmd.equals("delete")) {
						response = doDeleteDbObject(path, schema, type);
					} else if (cmd.equals("getSchemas")) {
						response = getSchemas();
					}
				}
			}
		}

		Enumeration en = req.getParameterNames();
		while (en.hasMoreElements()) {
			String elem = (String) en.nextElement();
			System.out.print("name = " + elem);
			System.out.println("; value = " + req.getParameterValues(elem)[0]);
		}
		if (response != null) {
			Writer w = new OutputStreamWriter(resp.getOutputStream(), "UTF-8");
			w.write(response.toString());
			w.close();
		}
		resp.setContentType("application/json");
		resp.setStatus(HttpServletResponse.SC_OK);
	}

	private String doDeleteDbObject(String o, String s, String t) {
		String retval = "{\"success\":false,\"error\":\"Nie można usunąć obiektu\"}";
		String path = "";
		int typ = DbOT.UNKNOWN;
		path = "root/schemy/" + s;
		try {
			typ = Integer.parseInt(t);
		} catch (NumberFormatException nfe) {

		}
		path += "/" + DbOT.getNameOfType(typ);
		if (dbObjects.containsKey(path)) {
			DbObject[] oldOfObject = dbObjects.get(path);
			DbObject[] listOfObject = null;
			Vector<DbObject> newList = new Vector<DbObject>();
			int status = DbConnection.ERROR;
			try {
				for (int i = 0; i < oldOfObject.length; i++) {
					if (oldOfObject[i] != null
							&& oldOfObject[i].getName().equals(o)) {
						typ = oldOfObject[i].getType();
						String name = oldOfObject[i].getName();
						String schema = oldOfObject[i].getSchema();
						if (typ == DbOT.VIEW) {
							status = DbMetaData.operateObject(u, new View(name,
									schema), DbConnection.DELETE);
						} else if (typ == DbOT.SEQUENCE) {
							status = DbMetaData.operateObject(u, new Sequence(
									name, schema), DbConnection.DELETE);
						} else if (typ == DbOT.FUNCTION) {
							status = DbMetaData.operateObject(u, new Function(
									name, schema), DbConnection.DELETE);
						} else if (typ == DbOT.PACKAGE) {
							status = DbMetaData.operateObject(u, new Package(
									name, schema), DbConnection.DELETE);
						} else if (typ == DbOT.PACKAGEBODY) {
							status = DbMetaData.operateObject(u,
									new PackageBody(name, schema),
									DbConnection.DELETE);
						} else if (typ == DbOT.PROCEDURE) {
							status = DbMetaData.operateObject(u, new Procedure(
									name, schema), DbConnection.DELETE);
						} else if (typ == DbOT.TABLE) {
							status = DbMetaData.operateObject(u, new Table(
									name, schema), DbConnection.DELETE);
						} else if (typ == DbOT.TRIGGER) {
							status = DbMetaData.operateObject(u, new Trigger(
									name, schema), DbConnection.DELETE);
						}

					} else {
						newList.add(oldOfObject[i]);
					}
					if (status == DbConnection.OK) {
						listOfObject = new DbObject[newList.size()];
						newList.toArray(listOfObject);
						retval = success();
						dbObjects.remove(path);
						dbObjects.put(path, listOfObject);
					} else {
						return out(retval);
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
				return out(error(e.getMessage()));
			}
		}
		retval = success();

		return out(retval);
	}

	private String success() {
		return "{\"success\":true}";
	}

	private String error(String error) {
		return out("{\"success\":false,\"error\":\"" + error + "\"}");
	}

	private String doGetDbObjects(String p, String schema) {
		JSONArray ja = new JSONArray();
		String path = "";
		int typ = DbOT.UNKNOWN;
		if (p.startsWith(ROOT + "/" + schema)) {// czy użytkownik chce otrzymać
												// kategorię obiektów
			path = p.replaceAll(ROOT + "/" + schema, ROOT + "/" + Schema.LABEL
					+ "/" + schema);
		} else {// w przeciwnym wypadku chce otrzymać obiekty danej kategorii
			try {
				typ = Integer.parseInt(p);
			} catch (NumberFormatException nfe) {

			}
			path = ROOT + "/" + Schema.LABEL + "/" + schema + "/"
					+ DbOT.getNameOfType(typ);
		}
		// szukanie w kategoriach struktur(schemy(wszystkie schemy)) )
		if (categories.containsKey(path)) {
			DbObject subdirs[] = (DbObject[]) categories.get(path);
			String category = path.substring(path.lastIndexOf("/") + 1);
			String icon = "folder";
			for (int i = 0; i < subdirs.length; i++) {
				try {
					JSONObject jo = new JSONObject();
					jo.put("text", subdirs[i].getName());
					icon = subdirs[i].getIcon();
					typ = subdirs[i].getType();
					category = subdirs[i].getName();
					jo.put("iconCls", icon);
					jo.put("disable", false);
					jo.put("t", String.valueOf(typ));
					if (categories.containsKey(path + "/" + category)
							|| dbObjects.containsKey(path + "/" + category)
							|| subdirs[i].is("leaf")) {
						jo.put("leaf", false);
					} else {
						jo.put("leaf", true);
					}
					ja.put(jo);
				} catch (JSONException e) {
					e.printStackTrace();
					return out(error(e.getMessage()));
				}
			}
		}

		// szukamy wszystkich obiektow (struktur)
		if (dbObjects.containsKey(path)) {
			String category = path.substring(path.lastIndexOf("/") + 1);
			try {
				if (category.equals(Table.LABEL)) {
					dbObjects.put(path, putIn(u, new Table(null, schema)));
				} else if (category.equals(View.LABEL)) {
					dbObjects.put(path, putIn(u, new View(null, schema)));
				} else if (category.equals(Procedure.LABEL)) {
					dbObjects.put(path, putIn(u, new Procedure(null, schema)));
				} else if (category.equals(Function.LABEL)) {
					dbObjects.put(path, putIn(u, new Function(null, schema)));
				} else if (category.equals(Trigger.LABEL)) {
					dbObjects.put(path, putIn(u, new Trigger(null, schema)));
				} else if (category.equals(Sequence.LABEL)) {
					dbObjects.put(path, putIn(u, new Sequence(null, schema)));
				} else if (category.equals(Package.LABEL)) {
					dbObjects.put(path, putIn(u, new Package(null, schema)));
				} else if (category.equals(PackageBody.LABEL)) {
					dbObjects
							.put(path, putIn(u, new PackageBody(null, schema)));
				}
			} catch (Exception e) {
				System.out.println(e);
				e.printStackTrace();
				return out(error(e.getMessage()));
			}
			DbObject fls[] = (DbObject[]) dbObjects.get(path);
			String icon = "file-txt";
			typ = DbOT.UNKNOWN;
			for (int i = 0; i < fls.length; i++) {
				try {
					if (fls[i].isUnknown()) {
						continue;
					}
					JSONObject jo = new JSONObject();
					jo.put("text", fls[i].getName());
					icon = fls[i].getIcon();
					jo.put("iconCls", icon);
					jo.put("disable", false);
					jo.put("leaf", true);
					// jo.put("qtip", "Opis obiektu: " + fls[i]);
					typ = fls[i].getType();
					jo.put("t", String.valueOf(typ));
					ja.put(jo);
				} catch (JSONException e) {
					e.printStackTrace();
					return out(error(e.getMessage()));
				}
			}
		}
		return out(ja.toString());
	}

	private DbObject[] putIn(DbUser u, DbObject o) throws Exception {
		List<DbObject> sy = new Vector<DbObject>();
		sy = DbMetaData.getObjects(u, o);
		DbObject[] syArray = new DbObject[sy.size()];
		for (int i = 0; i < sy.size(); i++) {
			syArray[i] = sy.get(i);
		}
		return syArray;
	}

	@SuppressWarnings("unused")
	private String getSchemaName(String path) {
		return out(path.substring(path.lastIndexOf("/") + 1));
	}

	private String getSchemas() {
		JSONObject schemyMainObject = new JSONObject();
		JSONArray schemy = new JSONArray();
		try {
			List<Schema> schemas = DbMetaData.getAllSchemas(u);
			Schema[] schemasArray = new Schema[schemas.size()];
			schemyMainObject.put("count", schemas.size());
			for (int i = 0; i < schemas.size(); i++) {
				schemasArray[i] = (Schema) schemas.get(i);
			}
			categories.put(ROOT + "/" + Schema.LABEL, schemasArray);
			for (int i = 0; i < schemasArray.length; i++) {
				categories.put(ROOT + "/" + Schema.LABEL + "/"
						+ schemasArray[i], new DbObject[] { new Table(),
						new View(), new Sequence(), new SourceType() });
				categories
						.put(ROOT + "/" + Schema.LABEL + "/" + schemasArray[i]
								+ "/" + SourceType.LABEL,
								new DbObject[] { new Package(),
										new PackageBody(), new Procedure(),
										new Function(), new Trigger() });

				dbObjects.put(ROOT + "/" + Schema.LABEL + "/" + schemasArray[i]
						+ "/" + Table.LABEL, new NONE[] { new NONE() });
				dbObjects.put(ROOT + "/" + Schema.LABEL + "/" + schemasArray[i]
						+ "/" + View.LABEL, new NONE[] { new NONE() });
				dbObjects.put(ROOT + "/" + Schema.LABEL + "/" + schemasArray[i]
						+ "/" + Sequence.LABEL, new NONE[] { new NONE() });
				dbObjects.put(ROOT + "/" + Schema.LABEL + "/" + schemasArray[i]
						+ "/" + SourceType.LABEL + "/" + Package.LABEL,
						new NONE[] { new NONE() });
				dbObjects.put(ROOT + "/" + Schema.LABEL + "/" + schemasArray[i]
						+ "/" + SourceType.LABEL + "/" + PackageBody.LABEL,
						new NONE[] { new NONE() });
				dbObjects.put(ROOT + "/" + Schema.LABEL + "/" + schemasArray[i]
						+ "/" + SourceType.LABEL + "/" + Procedure.LABEL,
						new NONE[] { new NONE() });
				dbObjects.put(ROOT + "/" + Schema.LABEL + "/" + schemasArray[i]
						+ "/" + SourceType.LABEL + "/" + Function.LABEL,
						new NONE[] { new NONE() });
				dbObjects.put(ROOT + "/" + Schema.LABEL + "/" + schemasArray[i]
						+ "/" + SourceType.LABEL + "/" + Trigger.LABEL,
						new NONE[] { new NONE() });

				JSONObject schema = new JSONObject();
				schema.put("name", schemasArray[i].getName());
				schemy.put(schema);
			}
			schemyMainObject.put("schemas", schemy);
		} catch (Exception e) {
			e.printStackTrace();
			return out(error(e.getMessage()));
		}
		return out(schemyMainObject.toString());
	}

	@SuppressWarnings("unused")
	private JSONObject getNode(DbObject o) {
		if (o == null) {
			return new JSONObject();
		}
		return getNode(o.getName(), o.getIcon(), o.is("disable"), o.is("leaf"),
				o.getType(), o.getString("remark"));
	}

	private JSONObject getNode(String text, String icon, boolean disable,
			boolean leaf, int t, String qtip) {
		JSONObject jo = new JSONObject();
		try {
			jo.put("text", text);
			jo.put("iconCls", icon);
			jo.put("disable", disable);
			jo.put("leaf", leaf);
			if (qtip != null && qtip.length() > 0) {
				jo.put("qtip", qtip);
			}
			jo.put("t", String.valueOf(t));
		} catch (Exception w) {
		}

		return jo;
	}

	private String out(String out) {
		return Unicode.encode(out.toString());
	}
}
